上一篇文章中讲述了,如何拿到crash,并且有几种方式来符号化crash文件。但是对文件本身还没有做对应的介绍。本节就来说说crash文件中各个字段对应的意义
Header
每个crash都有这样的header
Incident Identifier: 056AE3FF-0053-4B5F-8C42-5CFD2F4CE9F4
CrashReporter Key: 3c6d6b39fd3e330ce44828e97ab49661480cbb94
Hardware Model: iPhone10,3
Process: TestCrash [652]
Path: /private/var/containers/Bundle/Application/538B29B9-CED1-4969-913B-CB9344F70E94/TestCrash.app/TestCrash
Identifier: com.lianjia.TestCrash
Version: 1 (1.0)
Code Type: ARM-64 (Native)
Role: Foreground
Parent Process: launchd [1]
Coalition: com.lianjia.TestCrash [682]
Date/Time: 2018-08-10 17:43:31.2405 +0800
Launch Time: 2018-08-10 17:43:27.4744 +0800
OS Version: iPhone OS 11.4.1 (15G77)
Baseband Version: 1.93.00
Report Version: 104
apple 给出的解释如下:
Incident Identifier
: A unique identifier for the report. Two reports will never share the same Incident Identifier.CrashReporter Key
: An anonymized per-device identifier. Two reports from the same device will contain identical values.Beta Identifier
: A unique identifier for the combination of the device and vendor of the crashed application. Two reports for applications from the same vendor and from the same device will contain identical values. This field is only present in crash reports generated for applications distributed through TestFlight, and replaces the CrashReporter Key field.Process
: The executable name for the process that crashed. This matches the value for the CFBundleExecutable key in the application’s information property list.Version
: The version of the process that crashed. The value for this field is a concatenation of the crashed application’s CFBundleVersion and CFBundleVersionString.Code Type
: The target architecture of the process that crashed. This will be one of ARM-64, ARM, x86-64, or x86.Role
: The task_role assigned to the process at the time of termination.OS Version
: The OS version, including the build number, on which the crash occurred.
Exception Information
下一个分段中讲述的是异常信息: 这是一个未catch 的exception,值得注意的地方是这里没有termination reason。如果是手动handle uncatched exception时会看到当前异常的reason
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Triggered by Thread: 0
再来一个访问野指针的异常信息,这是从apple 文档上摘录的,在实际的crash文件中和上面的没有差别,也是没有reason
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000000
Termination Signal: Segmentation fault: 11
Termination Reason: Namespace SIGNAL, Code 0xb
Terminating Process: exc handler [0]
Triggered by Thread: 0
Exception Codes
: Processor specific information about the exception encoded into one or more 64-bit hexadecimal numbers. Typically, this field will not be present because the Crash Reporter parses the exception codes to present them as a human-readable description in the other fields.Exception Subtype
: The human-readable name of the exception codes.Exception Message
: Additional human-readable information extracted from the exception codes.Exception Note
: Additional information that is not specific to one exception type. If this field contains SIMULATED (this is NOT a crash) then the process did not crash, but was killed at the request of the system, typically the watchdog.Termination Reason
: Exit reason information specified when a process is terminated. Key system components, both inside and outside of a process, will terminate the process upon encountering a fatal error (e.g. a bad code signature, a missing dependent library, or accessing privacy sensitive information without the proper entitlement). macOS Sierra, iOS 10, watchOS 3, and tvOS 10 have adopted new infrastructure to record these errors, and crash reports generated by these operating systems list the error messages in the Termination Reason field.Triggered by Thread
: The thread on which the exception originated.
Additional Diagnostic Information
我在真实的crash中看到的额外信息和apple文档中的存在出入:我当前的crash
Application Specific Information:
abort() called
Filtered syslog:
None found
apple 文档中信息
额外信息1
Dyld Error Message:
Dyld Message: Library not loaded: @rpath/MyCustomFramework.framework/MyCustomFramework
Referenced from: /private/var/containers/Bundle/Application/CD9DB546-A449-41A4-A08B-87E57EE11354/TheElements.app/TheElements
Reason: no suitable image found.
额外信息2
Application Specific Information:
com.example.apple-samplecode.TheElements failed to scene-create after 19.81s (launch took 0.19s of total time limit 20.00s)
Elapsed total CPU time (seconds): 7.690 (user 7.690, system 0.000), 19% CPU
Elapsed application CPU time (seconds): 0.697, 2% CPU
额外信息中可能包含的内容有,当前app的信息,内核态的信息和动态库连接的信息
Application Specific Information
: framework error messages captured just before the process terminatedKernel Messages
: details about code-signature problemsDyld Error Messages
: error messages emitted by the dynamic linker
Backtraces
接下来就是我们熟悉的堆栈信息,堆栈都是逆序的,阅读堆栈信息有利于定位问题所在
Last Exception Backtrace:
(0x18308ad8c 0x1822445ec 0x183023750 0x182f5705c 0x1046d105c 0x18cdf964c 0x18cf1a870 0x18cdff700 0x18cf351a8 0x18ce7c9e0 0x18ce71890 0x18ce701d0 0x18d651d1c 0x18d6542c8 0x18d64d368 0x183033404 0x183032c2c 0x18303079c 0x182f50da8 0x184f36020 0x18cf70758 0x1046d1134 0x1829e1fc0)
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x0000000182b112e0 0x182aef000 + 140000
1 libsystem_pthread.dylib 0x0000000182cb66a8 0x182caf000 + 30376
2 libsystem_c.dylib 0x0000000182a7fd0c 0x182a1d000 + 404748
3 libc++abi.dylib 0x000000018221b2c8 0x18221a000 + 4808
4 libc++abi.dylib 0x000000018221b470 0x18221a000 + 5232
5 libobjc.A.dylib 0x00000001822448d4 0x18223c000 + 35028
6 libc++abi.dylib 0x000000018223537c 0x18221a000 + 111484
7 libc++abi.dylib 0x0000000182234f78 0x18221a000 + 110456
8 libobjc.A.dylib 0x00000001822447ac 0x18223c000 + 34732
9 CoreFoundation 0x0000000182f50e18 0x182f45000 + 48664
10 GraphicsServices 0x0000000184f36020 0x184f2b000 + 45088
11 UIKit 0x000000018cf70758 0x18cc53000 + 3266392
12 TestCrash 0x00000001046d1134 0x1046c8000 + 37172
13 libdyld.dylib 0x00000001829e1fc0 0x1829e1000 + 4032
Thread State
这是APP crash的时候,ARM64 构架CPU的32个寄存器的值, 虽然在堆栈的解析中不会用到这些值,但是在理解crash 奔溃的原因上有帮助,这里有一篇汇编的讲解
Thread 0 crashed with ARM Thread State (64-bit):
x0: 0x0000000000000000 x1: 0x0000000000000000 x2: 0x0000000000000000 x3: 0x00000001cc0eedb7
x4: 0x000000018223aabd x5: 0x000000016b7373f0 x6: 0x000000000000006e x7: 0x000000000000030b
x8: 0x0000000008000000 x9: 0x0000000004000000 x10: 0x0000000182cb2110 x11: 0x0000000000000003
x12: 0xffffffffffffffff x13: 0x0000000000000001 x14: 0x0000000000000000 x15: 0x0000000000000010
x16: 0x0000000000000148 x17: 0x0000000000000300 x18: 0x0000000000000000 x19: 0x0000000000000006
x20: 0x00000001b55dbb40 x21: 0x000000016b7373f0 x22: 0x0000000000000303 x23: 0x00000001b55dbc20
x24: 0x0000000000000001 x25: 0x00000001c00199f0 x26: 0x0000000000000000 x27: 0x0000000000000001
x28: 0x000000016b737b38 fp: 0x000000016b737350 lr: 0x0000000182cb66a8
sp: 0x000000016b737320 pc: 0x0000000182b112e0 cpsr: 0x00000000
Binary Images
二进制加载时的镜像地址,每次进程执行的时候都会生成全新的镜像地址。在crash的同时我们也需要镜像地址,因为使用handle的方式手动记录的crash exception中只存在堆栈的报错地址,并没有当前进程的镜像加载地址。手动handle的方式和 device crash文件中的堆栈的信息存在出入。镜像信息中包含的内容有:
- The binary image’s address space within the process.
- The binary name or bundle identifier of the binary (macOS only). In crash reports from macOS, a (+) is prepended if the binary is part of the OS.
- (macOS only) The binary’s short version string and bundle version, separated by a dash.
- (iOS only) The architecture of the binary image. A binary may contain multiple “slices”, one for each architecture it supports. Only one of these slices is loaded into the process.
- An UUID which uniquely identifies the binary image. This value changes with each build of the binary and is used to locate the corresponding dSYM file when symbolicating the crash report.
- The path to the binary on disk.
Binary Images:
0x1046c8000 - 0x1046d3fff TestCrash arm64 <1033c07aba8b3895bd20eb85e761bb49> /var/containers/Bundle/Application/538B29B9-CED1-4969-913B-CB9344F70E94/TestCrash.app/TestCrash
...
0x1ab910000 - 0x1ab940fff libclosured.dylib arm64 <e61ffac51cae3e1fb9eb6a6e2801777b> /usr/lib/closure/libclosured.dylib
补充信息
关于不同的信号代表的意思,搜索结果如下:
EXC_BAD_INSTRUCTION
此类异常通常由于线程执行非法指令导致。
在代码中修改了storyboard与outlet的对应关系,但是storyboard没有更新时发生过此crash。
与第三方库中方法冲突时发生过此crash。
调用系统方法时传入了不恰当的指针类型。
EXC_ARITHMETIC
代码中做除法时分母为零了会发生此问题。
EXC_BAD_ACCESS
此类型的Excpetion是我们最常碰到的Crash,通常用于访问了不该访问的内存导致。一般EXC_BAD_ACCESS后面的”()”还会带有补充信息。
SIGHUP
本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端不再关联。
登录Linux时,系统会分配给登录用户一个终端(Session)。在这个终端运行的所有程序,包括前台进程组和后台进程组,一般都属于这个 Session。当用户退出Linux登录时,前台进程组和后台有对终端输出的进程将会收到SIGHUP信号。这个信号的默认操作为终止进程,因此前台进 程组和后台有终端输出的进程就会中止。不过可以捕获这个信号,比如wget能捕获SIGHUP信号,并忽略它,这样就算退出了Linux登录, wget也 能继续下载。
此外,对于与终端脱离关系的守护进程,这个信号用于通知它重新读取配置文件。
SIGINT
程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl-C)时发出,用于通知前台进程组终止进程。
SIGQUIT
和SIGINT类似, 但由QUIT字符(通常是Ctrl-)来控制. 进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号。
SIGILL
执行了非法指令. 通常是因为可执行文件本身出现错误, 或者试图执行数据段. 堆栈溢出时也有可能产生这个信号。
SIGTRAP
由断点指令或其它trap指令产生. 由debugger使用。
SIGABRT
调用abort函数生成的信号退出,通常Foundation库中的容器为了保护状态正常会做一些检测,例如插入nil到数组中等会遇到此类错误。
SIGBUS
非法地址, 包括内存地址对齐(alignment)出错。比如访问一个四个字长的整数, 但其地址不是4的倍数。它与SIGSEGV的区别在于后者是由于对合法存储地址的非法访问触发的(如访问不属于自己存储空间或只读存储空间)。
与 SIGSEGV 不同的是,SIGSEGV 访问的是无效地址,而 SIGBUS 访问的是有效地址,但总线访问异常(如地址对齐问题)
SIGFPE
在发生致命的算术运算错误时发出. 不仅包括浮点运算错误, 还包括溢出及除数为0等其它所有的算术的错误。
SIGKILL
用来立即结束程序的运行. 本信号不能被阻塞、处理和忽略。如果管理员发现某个进程终止不了,可尝试发送这个信号。
SIGUSR1
留给用户使用
SIGSEGV
试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据,这种类型在切换了ARC以后应该已经很少见到了
SIGUSR2
留给用户使用
SIGPIPE
管道破裂。这个信号通常在进程间通信产生,比如采用FIFO(管道)通信的两个进程,读管道没打开或者意外终止就往管道写,写进程会收到SIGPIPE信号。此外用Socket通信的两个进程,写进程在写Socket的时候,读进程已经终止。
SIGALRM
时钟定时信号, 计算的是实际的时间或时钟时间. alarm函数使用该信号.
SIGTERM
程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程序自己正常退出,shell命令kill缺省产生这个信号。如果进程终止不了,我们才会尝试SIGKILL。
SIGCHLD
子进程结束时, 父进程会收到这个信号。
如果父进程没有处理这个信号,也没有等待(wait)子进程,子进程虽然终止,但是还会在内核进程表中占有表项,这时的子进程称为僵尸进程。这种情 况我们应该避免(父进程或者忽略SIGCHILD信号,或者捕捉它,或者wait它派生的子进程,或者父进程先终止,这时子进程的终止自动由init进程 来接管)。
SIGCONT
让一个停止(stopped)的进程继续执行. 本信号不能被阻塞. 可以用一个handler来让程序在由stopped状态变为继续执行时完成特定的工作. 例如, 重新显示提示符
SIGSTOP
停止(stopped)进程的执行. 注意它和terminate以及interrupt的区别:该进程还未结束, 只是暂停执行. 本信号不能被阻塞, 处理或忽略.
SIGTSTP
停止进程的运行, 但该信号可以被处理和忽略. 用户键入SUSP字符时(通常是Ctrl-Z)发出这个信号
SIGTTIN
当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN信号. 缺省时这些进程会停止执行.
SIGTTOU
类似于SIGTTIN, 但在写终端(或修改终端模式)时收到.
SIGURG
有”紧急”数据或out-of-band数据到达socket时产生.
SIGXCPU
超过CPU时间资源限制. 这个限制可以由getrlimit/setrlimit来读取/改变。
SIGXFSZ
当进程企图扩大文件以至于超过文件大小资源限制。
SIGVTALRM
虚拟时钟信号. 类似于SIGALRM, 但是计算的是该进程占用的CPU时间.
SIGPROF
类似于SIGALRM/SIGVTALRM, 但包括该进程用的CPU时间以及系统调用的时间.
SIGWINCH
窗口大小改变时发出.
SIGIO
文件描述符准备就绪, 可以开始进行输入/输出操作.
SIGPWR
Power failure
SIGSYS
非法的系统调用。
SEGV
Segmentation Violation,代表无效内存地址,比如空指针,未初始化指针,栈溢出等
关键点注意
在以上列出的信号中,程序不可捕获、阻塞或忽略的信号有:SIGKILL,SIGSTOP
不能恢复至默认动作的信号有:SIGILL,SIGTRAP
默认会导致进程流产的信号有:SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGIOT,SIGQUIT,SIGSEGV,SIGTRAP,SIGXCPU,SIGXFSZ
默认会导致进程退出的信号有:
SIGALRM,SIGHUP,SIGINT,SIGKILL,SIGPIPE,SIGPOLL,SIGPROF,SIGSYS,SIGTERM,SIGUSR1,SIGUSR2,SIGVTALRM
默认会导致进程停止的信号有:SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU
默认进程忽略的信号有:SIGCHLD,SIGPWR,SIGURG,SIGWINCH
此外,SIGIO在SVR4是退出,在4.3BSD中是忽略;SIGCONT在进程挂起时是继续,否则是忽略,不能被阻塞。
signal handle
根据上面的结论,在crash handle 信号中主要handle的对象有:
signal(SIGHUP, signalHandler);
signal(SIGINT, signalHandler);
signal(SIGQUIT, signalHandler);
signal(SIGABRT, signalHandler);
signal(SIGILL, signalHandler);
signal(SIGSEGV, signalHandler);
signal(SIGFPE, signalHandler);
signal(SIGBUS, signalHandler);
signal(SIGPIPE, signalHandler);